#pragma once
#include <iostream>
#include <assert.h>
#include <stdlib.h>
#include <math.h>
using namespace std;
template <class k , class v>
struct Node {
	Node<k,v>* _left;
	Node<k, v>* _right;
	Node<k, v>* _parent;
	pair<k, v> _kv;
	int _bf;
	Node(const pair<k,v>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		,_kv(kv)
		, _bf(0)
	{}
};
template <class k, class v>
class AVLTree {
public:
	AVLTree()
		: _root(nullptr)
	{}
	void RotateL(Node<k, v>* parent) {
		Node<k, v>* subr = parent->_right;
		Node<k, v>* subrl = subr->_left;
		Node<k, v>* PPNode = parent->_parent;
		parent->_right = subrl;
		if(subrl)
		{
			subrl->_parent = parent;
		}
		subr->_left = parent;
		parent->_parent = subr;
		if (parent == _root)
		{
			_root = subr;
			_root->_parent = nullptr;
		}
		else
		{
			if (PPNode->_left == parent)
			{
				PPNode->_left = subr;
			}
			else if (PPNode->_right == parent)
			{
				PPNode->_right = subr;
			}
			subr->_parent = PPNode;
		}
		subr->_bf = parent->_bf = 0;
	}
	void RotateR(Node<k, v>* parent) {
		Node<k, v>* subl = parent->_left;
		Node<k, v>* sublr = subl->_right;
		Node<k, v>* PPNode = parent->_parent;
		parent->_left = sublr;
		if (sublr)
		{
			sublr->_parent = parent;
		}
		subl->_right = parent;
		parent->_parent = subl;
		if (parent == _root)
		{
			_root = subl;
			subl->_parent = nullptr;
		}
		else
		{
			if (PPNode->_left == parent)
			{
				PPNode->_left = subl;
			}
			else if (PPNode->_right == parent)
			{
				PPNode->_right = subl;
			}
			subl->_parent = PPNode;
		}
		subl->_bf = parent->_bf = 0;
	}
	void RotateRL(Node<k, v>* parent)
	{
		Node<k, v>* subr = parent->_right;
		Node<k, v>* subrl = subr->_left;
		int bf = subrl->_bf;
		RotateR(subr);
		RotateL(parent);
		if (bf == -1)
		{
			subrl->_bf = 0;
			subr->_bf = 1;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			subrl->_bf = 0;
			subr->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == 0)
		{
			subrl->_bf = 0;
			subr->_bf = 0;
			parent->_bf = 0;
		}
		else {
			assert(false);
		}
	}
	void RotateLR(Node<k, v>* parent)
	{
		Node<k, v>* subl = parent->_left;
		Node<k, v>* sublr = subl->_right;
		int bf = sublr->_bf;
		RotateL(parent->_left);
		RotateR(parent);
		if (bf == -1)
		{
			sublr->_bf = 0;
			subl->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 1)
		{
			sublr->_bf = 0;
			subl->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			sublr->_bf = 0;
			subl->_bf = 0;
			parent->_bf = 0;
		}
		else {
			assert(false);
		}
	}
	bool insert(const pair<k, v>& val)
	{
		if (_root == nullptr)
		{
			_root = new Node<k, v>(val);
			return true;
		}
		else {
			Node<k, v>* parent = nullptr;
			Node<k, v>* cur = _root;
			while (cur)
			{
				if (val.first < cur->_kv.first)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (val.first > cur->_kv.first)
				{
					parent = cur;
					cur = cur->_right;
				}
				else 
					return false;
			}
			cur = new Node<k, v>(val);
			if (val.first < parent->_kv.first)
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			cur->_parent = parent;
			while (parent)
			{
				if (cur == parent->_left)
				{
					parent->_bf--;
				}
				else
				{
					parent->_bf++;
				}
				if (parent->_bf == 0)
					break;
				else if (parent->_bf == 1 || parent->_bf == -1)
				{
					cur = parent;
					parent = parent->_parent;
				}
				else if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotateL(parent);
					break;
				}
				else if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);
					break;
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
					break;
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
					break;
				}
			}
			return true;
		}
	}
	void InOrder() {
		_InOrder(_root);
	}
	int Height(Node<k, v>* node) {
		if (node == nullptr)
		{
			return 0;
		}
		return max(Height(node->_left), Height(node->_right)) + 1;
	}
	bool IsAVLTree() {
		return _IsAVLTree(_root);
	}
private:
	bool _IsAVLTree(Node<k, v>* root) {
		if (root == nullptr)
		{
			return true;
		}
		int left = Height(root->_left);
		int right = Height(root->_right);
		if (abs(left - right) >= 2)
		{
			cout  << endl << root->_kv.first << endl;
			return false;
		}
		return _IsAVLTree(root->_left) && _IsAVLTree(root->_right);
	}
	void _InOrder(Node<k, v>* node) {
		if (node == nullptr)
		{
			return;
		}
		_InOrder(node->_left);
		cout << node->_kv.first << ":" << node->_kv.second << "    ";
		_InOrder(node->_right);
	}
	Node<k, v>* _root = nullptr;
};